#pragma once

#include <string>
#include <tchar.h>
#include <gtl/lib/lib.h>
#include <gtl/mpl/fun.h>
#include <gtl/string/str.h>
#include <gtl/io/app_path.h>

namespace gtl
{

namespace media
{

struct media_info 
{
	std::string demuxer;

	std::string videofmt;
	unsigned long vbitrate;
	unsigned long width;
	unsigned long height;
	double fps;
	double aspect;

	std::string audiofmt;
	unsigned long abitrate;
	unsigned long srate;
	unsigned short channels;

	double duration;
};

// const wchar_t* (__stdcall* MediaInfo_Inform)(void*, size_t);

class info
{
public:
	enum stream_e
	{
		Stream_General,
		Stream_Video,
		Stream_Audio,
		Stream_Text,
		Stream_Chapters,
		Stream_Image,
		Stream_Menu,
		Stream_Max
	};

	enum info_e
	{
		Info_Name,
		Info_Text,
		Info_Measure,
		Info_Options,
		Info_Name_Text,
		Info_Measure_Text,
		Info_Info,
		Info_HowTo,
		Info_Domain,
		Info_Max
	};

public:
	info()
	{
		m_handle = NULL;
		m_opened = false;
	}

	info(const gtl::tchar* path)
	{
		m_handle = NULL;
		m_opened = open(path);
	}

	~info()
	{
		close();
	}

public:
	bool init(const gtl::tchar* name = _T("MediaInfo.dll"))
	{
		if(m_handle != NULL)
			return true;

		if(name == NULL)
			return false;

		if(m_lib == NULL)
		{
			m_lib.load(gtl::io::get_app_path<gtl::tstr>() + name);
			if(m_lib == NULL)
				return false;
		}

		try
		{
			m_handle = m_lib.call<gtl::mpl::fun<void* (__stdcall*)()>>("MediaInfo_New");
			return m_handle != NULL;
		}
		catch(...)
		{
		}

		return false;
	}

public:
	bool open(const gtl::tchar* path)
	{
		if(!init())
			return false;

		try
		{
			close();

			size_t nRet = m_lib.call<gtl::mpl::fun<size_t (__stdcall*)(void*, const wchar_t*)>>("MediaInfo_Open", m_handle, path);
			return nRet > 0;
		}
		catch(...)
		{
		}

		return false;
	}

	void close()
	{
		try
		{
			m_lib.call<gtl::mpl::fun<void (__stdcall*)(void*)>>("MediaInfo_Close", m_handle);
		}
		catch(...)
		{
		}
	}

	const gtl::tchar* get(stream_e strm_kind, const gtl::tchar* parameter, info_e info_kind = Info_Text, info_e search_kind = Info_Name, size_t strm_num = 0)
	{
		if(m_handle == NULL)
			return NULL;

		try
		{
			typedef const wchar_t* (__stdcall* MediaInfo_Get)(void*, int, size_t, const wchar_t*, int, int);
			return m_lib.call<gtl::mpl::fun<MediaInfo_Get>>("MediaInfo_Get", m_handle, strm_kind, strm_num, parameter, info_kind, search_kind);
		}
		catch(...)
		{
		}

		return NULL;
	}

public:
	// file info
	const gtl::tchar* file_format()
	{
		return get(Stream_General, _T("Format"));
	}

	int duration()
	{
		return gtl::tstr_warp(get(Stream_General, _T("Duration"))).cast<int>();
	}

	const gtl::tchar* duration(bool to_str)
	{
		return get(Stream_General, to_str ? _T("Duration/String") : _T("Duration"));
	}

public:
	// video info
	const gtl::tchar* video_format()
	{
		return get(Stream_Video, _T("Format"));
	}

	const gtl::tchar* vcodec_id()
	{
		return get(Stream_Video, _T("CodecID"));
	}

	const gtl::tchar* vcodec_info()
	{
		return get(Stream_Video, _T("CodecID/Info"));
	}

	const gtl::tchar* vcodec_profile()
	{
		return get(Stream_Video, _T("Format_Profile"));
	}

	const gtl::tchar* vbitrate(bool to_str = false)
	{
		return get(Stream_Video, to_str ? _T("BitRate/String") : _T("BitRate"));
	}

	int width()
	{
		return gtl::tstr_warp(get(Stream_Video, _T("Width"))).cast<int>();
	}

	int height()
	{
		return gtl::tstr_warp(get(Stream_Video, _T("Height"))).cast<int>();
	}

	gtl::tstr resolution()
	{
		return gtl::tstr(get(Stream_Video, _T("Width"))) + _T("x") + gtl::tstr(get(Stream_Video, _T("Height")));
	}

	const gtl::tchar* aspect_ratio(bool to_str = false)
	{
		return get(Stream_Video, to_str ? _T("DisplayAspectRatio/String") : _T("DisplayAspectRatio"));
	}

	float fps()
	{
		return gtl::tstr_warp(get(Stream_Video, _T("FrameRate"))).cast<float>();
	}

	const gtl::tchar* fps(bool to_str)
	{
		return get(Stream_Video, to_str ? _T("FrameRate/String") : _T("FrameRate"));
	}

	int video_duration()
	{
		return gtl::tstr_warp(get(Stream_Video, _T("Duration"))).cast<int>();
	}

	const gtl::tchar* video_duration(bool to_str)
	{
		return get(Stream_Video, to_str ? _T("Duration/String") : _T("Duration"));
	}

public:
	// audio info
	const gtl::tchar* audio_format()
	{
		return get(Stream_Audio, _T("Format"));
	}

	const gtl::tchar* acodec_id()
	{
		return get(Stream_Audio, _T("CodecID"));
	}

	const gtl::tchar* acodec_info()
	{
		return get(Stream_Audio, _T("CodecID/Info"));
	}

	const gtl::tchar* acodec_profile()
	{
		return get(Stream_Audio, _T("Format_Profile"));
	}

	const gtl::tchar* abitrate(bool to_str = false)
	{
		return get(Stream_Audio, to_str ? _T("BitRate/String") : _T("BitRate"));
	}

	const gtl::tchar* channels(bool to_str = false)
	{
		return get(Stream_Audio, to_str ? _T("Channel(s)/String") : _T("Channel(s)"));
	}

	const gtl::tchar* srate(bool to_str = false)
	{
		return get(Stream_Audio, to_str ? _T("SamplingRate/String") : _T("SamplingRate"));
	}

	const gtl::tchar* bit_depth(bool to_str = false)
	{
		return get(Stream_Audio, to_str ? _T("BitDepth/String") :  _T("BitDepth"));
	}

	int audio_duration()
	{
		return gtl::tstr_warp(get(Stream_Audio, _T("Duration"))).cast<int>();
	}

	const gtl::tchar* audio_duration(bool to_str)
	{
		return get(Stream_Audio, to_str ? _T("Duration/String") : _T("Duration"));
	}

protected:
	bool	 m_opened;
	void*	 m_handle;
	gtl::lib m_lib;
};

} // end of namespace media

} // end of namespace gtl